#include "USerial.h"
#include "MyEventQueue.hpp"
#include "Handler.hpp"
#include "dbt.h"
#include "util.hpp"
#include "Defmt.hpp"

// #include "ide/clang/clang.h"

// GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
//                       0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };


static USerial *s_app = nullptr;
void SetupAppPointer(USerial *app)
{
	s_app = app;
}

USerial *GetAppPointer()
{
	return s_app;
}

INITBLOCK { // This has to be at file level scope
	Value::Register<MyEvent>(); // need to register value type integer id to allow serialization
	Ctrl::SetDarkThemeEnabled(false);
}


void MySerialListener::onReadEvent(const char *portName, unsigned int readBufferLen)
{
	if (readBufferLen == 0 || GetAppPointer() == nullptr)
	{
		return;
	}
	if (readBufferLen > 0)
	{
		//find the serial port
		CSerialPort *serial = GetAppPointer()->GetSerial(portName);
		Upp::String readBuff(0, readBufferLen);
		serial->readData((void*)readBuff.begin(), readBufferLen);
		String port = portName;
		MyPostEvent(EVENT_SERIAL_DATA_RECV, {port, readBuff});
	}
}
void MySerialListener::OnSendEvent(const MyEvent &e)
{
	if (GetAppPointer() == nullptr)
	{
		return;
	}
	CSerialPort *serial = GetAppPointer()->GetSerial(e.event_data[0]);
	if (serial != nullptr)
	{
		serial->writeData(e.event_data[1], e.event_data[1].GetLength());
	}
}
void MySerialListener::OnOpenEvent(const MyEvent &e)
{
	//open serial port
	//如果串口已经打开，则重新打开
	//args: port name, baud rate
	if (GetAppPointer() == nullptr)
	{
		return;
	}
	//先找到哪个UI触发的
	CSerialPort *serial = GetAppPointer()->GetUISerial(e.event_data[0]);
	if (serial == nullptr)
	{
		//not found
		return;
	}
	//如果串口已经打开，直接设置波特率
	int baudrate = atoi(e.event_data[1]);
	if (serial->isOpen())
	{
		//convert string to int
		serial->setBaudRate(baudrate);
	}
	else
	{
		//open serial port
		//init
		serial->init(e.event_data[0], baudrate);
		if (serial->open())
		{
			serial->connectReadEvent(this);
			String error = "0";
			MyPostEvent(EVENT_SERIAL_OPENED, {e.event_data[0], error});
		}
		else
		{
			//open failed
			//show error message
			String type = "Error";
			String msg = "Open serial ";
			msg << e.event_data[0] << " failed(" << serial->getLastError() << ")";
			MyPostEvent(EVENT_UI_MESSAGE_BOTTOM, {type, msg});
			String error;
			error << serial->getLastError();
			MyPostEvent(EVENT_SERIAL_OPENED, {e.event_data[0], error});
		}
	}
}
void MySerialListener::OnCloseEvent(const MyEvent &e)
{
	if (GetAppPointer() == nullptr)
	{
		return;
	}
	//find the serial port
	CSerialPort *serial = GetAppPointer()->GetUISerial(e.event_data[0]);
	if (serial == nullptr)
	{
		//not found
		return;
	}
	if (serial->isOpen())
	{
		serial->close();
		serial->disconnectHotPlugReadEvent();
		String error = "0";
		MyPostEvent(EVENT_SERIAL_CLOSED, {e.event_data[0], error});
	}
}

void USerial::CreateDisplay()
{
	m_displays.Add();
}

USerial::USerial()
{
	m_openPortName.Clear();

	CtrlLayout(*this, "USerial");
	Zoomable().Sizeable().CenterScreen();
	grid.SetColumns(1);

	m_filterDlg.Close();

	// mainDisplay.Highlight("log");	
	CreateDisplay();
	m_displays[0].serial.connectHotPlugEvent(this);
	
	//add display to splitter
	grid.Add(m_displays[0].ui.SizePos());
	//setup menu
	MainMenu(menu);
	//setup toolbar
	// bar.Add(FormEditImg::New(), THISBACK(NewSerialUI)).Tip(t_("Create new form..."));

	StartEventQueueThread();
	MyRegisterEvent(EVENT_UI_MESSAGE_BOTTOM, THISBACK(OnMessageBottom));

	//init event handler
	// m_eventHandlers.Add(new LineEventHandler());
	
	// m_filterDlg.SetMainEditor(&mainDisplay);
}

void USerial::NewSerialUI()
{
	grid.SetColumns(2);
	//create a new display
	CreateDisplay();
	//add to splitter
	grid.Add(m_displays[m_displays.GetCount() - 1].ui.SizePos());
	// splitter.Horz();
}

void USerial::OnMessageBottom(const MyEvent &e)
{
	GuiLock _;
	if (e.event_data[0] == "Error")
	{
		m_msgCtrl.Bottom().Error(*this, e.event_data[0]);
	}
	else if (e.event_data[0] == "Warning")
	{
		m_msgCtrl.Bottom().Warning(*this, e.event_data[0]);
	}
	else if (e.event_data[0] == "Info")
	{
		m_msgCtrl.Bottom().Information(*this, e.event_data[0]);
	}
}


USerial::~USerial()
{
	// Upp::Thread::ShutdownThreads();
	//delete all event handlers
}


void USerial::Quit()
{
	MyPostEvent(EVENT_QUIT, {});
	Break();
	//close serial port
	for (int i = 0; i < m_displays.GetCount(); i++)
	{
		if (m_displays[i].serial.isOpen())
		{
			m_displays[i].serial.close();
		}
	}
	// for (int i = 0; i < m_eventHandlers.GetCount(); i++)
	// {
	// 	delete m_eventHandlers[i];
	// }
}

void USerial::ProjectPath()
{
	Defmt::LogManager log;	
	String path = "D:/tool/upp/MyApps";
	log.SetPath(path);
}

void USerial::FileMenu(Bar& bar)
{
	bar.Add("Filter", THISBACK(OpenCloseFilterDlg));
	bar.Add("New", THISBACK(NewSerialUI));
	// bar.Add("Vertical", THISBACK(Vertical));
	// bar.Add("Horizontal", THISBACK(Horizontal));
	bar.Add("Project_path", THISBACK(ProjectPath));
	bar.Add("Exit", THISBACK(Quit));
}

void USerial::MainMenu(Bar& bar)
{
	bar.Add("File", THISBACK(FileMenu));
}


void USerial::onHotPlugEvent(const char *portName, int isAdd)
{
	MyPostEvent(EVENT_HW_HOTPLUG, {});
}

// LRESULT USerial::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
// {
// 	LRESULT ret = TopWindow::WindowProc(message, wParam, lParam);
// 	static HDEVNOTIFY hDevNotify;
// 	switch (message)
// 	{
// 		case WM_CREATE:
// 		{
// 			//register device notification
// 			DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
// 			ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
// 			NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
// 			NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
// 			NotificationFilter.dbcc_classguid = WceusbshGUID;
// 			hDevNotify = RegisterDeviceNotification(GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
// 			SetAlpha(255);
// 			break;
// 		}
// 		case WM_CLOSE:
// 		{
// 			//unregister device notification
// 			if (hDevNotify)
// 			{
// 				UnregisterDeviceNotification(hDevNotify);
// 			}
// 			break;
// 		}
// 		case WM_DEVICECHANGE:
// 		{
// 			freshPortList();
// 			break;
// 		}
// 	}
// 	return ret;
// }

GUI_APP_MAIN
{
	#if 0
	{
		//test clang
		Clang clang;
		clang_CXIndex_setGlobalOptions(clang.index, CXGlobalOpt_ThreadBackgroundPriorityForIndexing);
		String path = GetExeDirFile("test.c");
		String blitz;
		String includes;
		String defines;
		clang.Parse(path, blitz, includes, defines,
					CXTranslationUnit_KeepGoing|
					CXTranslationUnit_DetailedPreprocessingRecord|PARSE_FILE);
		// ClangVisitor v;
		// if(clang.tu) {
		// 	v.Do(clang.tu);
		// }
		DUMP(includes);
		DUMP(defines);

	}
	#endif

	USerial app;
	//create log directory
	if (!DirectoryExists("log"))
	{
		DirectoryCreate("log");
	}
	LoadFromFile(app);
	SetupAppPointer(&app);
	app.Run();
	SetupAppPointer(nullptr);
	StoreToFile(app);
}
